﻿
using System;
using System.Collections.Generic;
using System.Data;
using System.Windows.Forms;
using System.Net;
using System.Xml;
using ComponentFactory.Krypton.Toolkit;
using Microsoft.Practices.ServiceLocation;
using Castle.Windsor;
using log4net;
using OAuth.Net.Consumer;
using OAuth.Net.Components;
using EndPoint = OAuth.Net.Consumer.EndPoint;
using System.Collections.Specialized;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Linq;
using OAuth.Net.Common;
using Newtonsoft.Json.Linq;
using System.Diagnostics;

namespace TestApi
{
    /// <summary>
    ///     
    /// </summary>
    public partial class ApiForm : KryptonForm
    {
        /// <summary>
        /// 
        /// </summary>
        private static readonly ILog _log = LogManager.GetLogger(typeof(ApiForm));

        /// <summary>
        /// 
        /// </summary>
        readonly Dictionary<string, LogViewItem> _logRequests = new Dictionary<string, LogViewItem>();

        /// <summary>
        /// 
        /// </summary>
        readonly Dictionary<string, LogViewItem> _logResponses = new Dictionary<string, LogViewItem>();

        /// <summary>
        ///     
        /// </summary>
        ApiConfigs _configs;

        /// <summary>
        /// 
        /// </summary>
        IWindsorContainer _container;

        /// <summary>
        ///     
        /// </summary>
        public ApiForm()
        {
            InitializeComponent();
            Text = "Not logged in";
            InitializeLogView();
            InitializeResultsView();
            InitializeApiConfigs();
        }

        /// <summary>
        /// 
        /// </summary>
        private void InitializeApiConfigs()
        {
            _container = ServiceLocator.Current.GetService(typeof(IWindsorContainer)) as IWindsorContainer;
            _configs = _container.Resolve<ApiConfigs>();
            _usernameTextBox.Text = _configs.XAuthUsername;
            _passwordTextBox.Text = _configs.XAuthPassword;
            _consumerKeyTextBox.Text = _configs.OAuthConsumerKey;
            _consumerSecretTextBox.Text = _configs.OAuthConsumerSecret;
            _testEndpointTextBox.Text = _configs.TestEndpointUrl;
        }

        /// <summary>
        /// 
        /// </summary>
        private void InitializeResultsView()
        {
            var dt = new DataTable();
            dt.Columns.Add(new DataColumn("key"));
            dt.Columns.Add(new DataColumn("order"));
            dt.Columns.Add(new DataColumn("content"));
            _resultsGridView.AutoGenerateColumns = false;
            _resultsGridView.DataSource = dt;
        }

        /// <summary>
        /// 
        /// </summary>
        private void InitializeLogView()
        {
            var dt = new DataTable();
            dt.Columns.Add(new DataColumn("key"));
            dt.Columns.Add(new DataColumn("order"));
            dt.Columns.Add(new DataColumn("content"));
            _logGridView.AutoGenerateColumns = false;
            _logGridView.DataSource = dt;
        }

        #region [ Web Method ]

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnWebLoginButtonClick(object sender, EventArgs e)
        {

            try
            {
                var service = OAuthService.Create(
                    new EndPoint(_configs.OAuthRequestTokenUrl),
                    new Uri(_configs.OAuthAuthorizeUrl),
                    new EndPoint(_configs.OAuthAccessTokenUrl),
                    false,
                    string.Empty,
                    "HMAC-SHA1",
                    "1.0",
                    new OAuthConsumer(_consumerKeyTextBox.Text, _consumerSecretTextBox.Text)
                );

                // create authorize request
                var request = OAuthRequest.Create(
                    new EndPoint(_configs.OAuthAuthorizeUrl, "GET"),
                    service);

                request.BeforeSendHttpWebRequest += OnBeforeSendHttpWebRequest;
                request.AuthorizationHandler = (s, args) =>
                {
                    // the following line will load the URL in the users default browser.
                    //Process.Start(authorizeUrl);

                    _tokenTextBox.Text = request.RequestToken != null ? request.RequestToken.Token : string.Empty;
                    _tokenSecretTextBox.Text = request.RequestToken != null ? request.RequestToken.Secret : string.Empty;
                };

                request.Authorize();
            }
            catch (Exception ex)
            {
                KryptonMessageBox.Show(ex.Message);
            }
        }

        private void OnContinueButtonClick(object sender, EventArgs e)
        {

            //try
            //{

            //    using (var consumer = new AuthService(_consumerKeyTextBox.Text, _consumerSecretTextBox.Text))
            //    {
            //        consumer.RequestSending += OnConsumerRequestSending;
            //        consumer.ResponseReceived += OnConsumerResponseReceived;
            //        var auth = consumer.RetrieveToken(_frob);
            //        Text = string.Format("Hello <{0}>", auth.Profile.FullName);
            //        _tokenTextBox.Text = auth.Token;
            //    }
            //}
            //catch (Exception ex)
            //{
            //    KryptonMessageBox.Show(ex.Message);
            //}
        }

        #endregion

        #region [ Direct Method ]

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnXAuthLoginButtonClick(object sender, EventArgs e)
        {
            try
            {
                var service = OAuthService.Create(
                    new EndPoint(_configs.OAuthRequestTokenUrl),
                    new Uri(_configs.OAuthAuthorizeUrl),
                    new EndPoint(_configs.OAuthAccessTokenUrl),
                    false,
                    string.Empty,
                    "HMAC-SHA1",
                    "1.0",
                    new OAuthConsumer(_consumerKeyTextBox.Text, _consumerSecretTextBox.Text)
                );

                // consume math service
                var request = OAuthRequest.Create(
                    new EndPoint(_configs.XAuthLoginUrl,
                        _getRadioButton.Checked ? "GET" : "POST"),
                    service);

                request.BeforeSendHttpWebRequest += OnBeforeSendHttpWebRequest;

                var passwordHexaEncoded = string.Empty;
                using (var hash = new SHA1Managed())
                {
                    byte[] passwordBytes = Encoding.UTF8.GetBytes(_passwordTextBox.Text);
                    byte[] hashBytes = hash.ComputeHash(passwordBytes);

                    Array.ForEach(hashBytes, b =>
                    {
                        var hexaByte = b.ToString("X").ToLower();
                        // lowercase for compatibility on case-sensitive systems
                        passwordHexaEncoded += (hexaByte.Length == 1 ? "0" : "") + hexaByte;
                    });
                }

                var parameters = new NameValueCollection()
                {
                    { "x_auth_mode", "client_auth" },
                    { "x_auth_username", _usernameTextBox.Text },
                    { "x_auth_password", passwordHexaEncoded },
                };

                if (_xmlRadioButton.Checked)
                {
                    parameters.Add("format", "xml");
                }
                else if (_jsonRadioButton.Checked)
                {
                    parameters.Add("format", "json");
                }
                else if (_plainTextRadioButton.Checked)
                {
                    parameters.Add("format", "plain-text");
                }

                var response = request.GetPublicResource(parameters);
                request.BeforeSendHttpWebRequest -= OnBeforeSendHttpWebRequest;
                OnTokenReceived(response);
            }
            catch (Exception ex)
            {
                KryptonMessageBox.Show(ex.Message);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="reponse"></param>
        private void OnTokenReceived(OAuthResponse response)
        {
            var result = ParseResponseAndLog(response);
            var data = new NameValueCollection();
            if (string.Compare(response.ProtectedResource.ContentType, "text/html", false) == 0)
            {
                var pairs = result.Item1.Split(new char[] { '&' });

                Array.ForEach(pairs, pair =>
                {
                    var parts = pair.Split(new char[] { '=' });
                    if (parts.Length == 2)
                    {
                        data.Add(parts[0], parts[1]);
                    }
                    else
                        if (parts.Length == 1)
                        {
                            data.Add(parts[0], string.Empty);
                        }
                });

                if (data.AllKeys.Contains("token"))
                {
                    _tokenTextBox.Text = data["token"];
                }

                if (data.AllKeys.Contains("token_secret"))
                {
                    _tokenSecretTextBox.Text = data["token_secret"];
                }
            }
            else if (string.Compare(response.ProtectedResource.ContentType, "text/xml", false) == 0)
            {
                var doc = new XmlDocument();
                doc.LoadXml(result.Item1);

                var tokenNode = doc.SelectSingleNode("//rsp/result/token");
                var tokenSecretNode = doc.SelectSingleNode("//rsp/result/token_secret");

                _tokenTextBox.Text = tokenNode != null ? tokenNode.InnerText : string.Empty;
                _tokenSecretTextBox.Text = tokenSecretNode != null ? tokenSecretNode.InnerText : string.Empty;
            }
            else if (string.Compare(response.ProtectedResource.ContentType, "application/json", false) == 0)
            {
                var token = JObject.Parse(result.Item1);
                var tokenNode = token.SelectToken("token");
                var tokenSecretNode = token.SelectToken("token_secret");
                _tokenTextBox.Text = tokenNode != null ? tokenNode.ToString() : string.Empty;
                _tokenSecretTextBox.Text = tokenSecretNode != null ? tokenSecretNode.ToString() : string.Empty;
            }
        }

        #endregion

        /// <summary>
        ///     
        /// </summary>
        private void OnAddArgButtonClick(object sender, EventArgs e)
        {

            if (string.IsNullOrEmpty(_argNameTextBox.Text))
            {
                return;
            }
            _argsDataGridView.Rows.Add(new object[] { _argNameTextBox.Text, _argValueTextBox.Text });
        }

        /// <summary>
        ///     
        /// </summary>
        private void OnRemoveLastArgButtonClick(object sender, EventArgs e)
        {

            if (_argsDataGridView.Rows.Count > 0)
            {
                _argsDataGridView.Rows.RemoveAt(_argsDataGridView.Rows.Count - 1);
            }
        }

        /// <summary>
        ///     
        /// </summary>
        private void OnClearAllArgsButtonClick(object sender, EventArgs e)
        {
            _argsDataGridView.Rows.Clear();
        }

        /// <summary>
        ///     
        /// </summary>
        private void OnActionButtonClick(object sender, EventArgs e)
        {

            if (string.IsNullOrEmpty(_testEndpointTextBox.Text))
            {
                return;
            }

            try
            {
                var consumer = new OAuthConsumer(_consumerKeyTextBox.Text, 
                    _consumerSecretTextBox.Text);
                var service = OAuthService.Create(
                    new EndPoint(_configs.OAuthRequestTokenUrl),
                    new Uri(_configs.OAuthAuthorizeUrl),
                    new EndPoint(_configs.OAuthAccessTokenUrl),
                    false,
                    string.Empty,
                    "HMAC-SHA1",
                    "1.0",
                    consumer
                );

                var requestToken = new EmptyToken(_consumerKeyTextBox.Text, TokenType.Request);
                var accessToken = new OAuthToken(TokenType.Access, _tokenTextBox.Text, 
                    _tokenSecretTextBox.Text, consumer);

                // consume math service
                var request = OAuthRequest.Create(
                    new EndPoint(_testEndpointTextBox.Text,
                        _getRadioButton.Checked ? "GET" : "POST"),
                    service, requestToken, accessToken);

                request.BeforeSendHttpWebRequest += OnBeforeSendHttpWebRequest;

                var parameters = new NameValueCollection();

                foreach (var row in _argsDataGridView.Rows)
                {
                    parameters.Add(((DataGridViewRow)row).Cells[0].Value.ToString(),
                        ((DataGridViewRow)row).Cells[1].Value.ToString());
                }

                if (_xmlRadioButton.Checked)
                {
                    parameters.Add("format", "xml");
                }
                else if (_jsonRadioButton.Checked)
                {
                    parameters.Add("format", "json");
                }
                else if (_plainTextRadioButton.Checked)
                {
                    parameters.Add("format", "plain-text");
                }

                var response = request.GetResource(parameters);
                request.BeforeSendHttpWebRequest -= OnBeforeSendHttpWebRequest;
                OnTestResponseReceived(response);
            }
            catch (Exception ex)
            {
                KryptonMessageBox.Show(ex.Message);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="response"></param>
        private void OnTestResponseReceived(OAuthResponse response)
        {
            ParseResponseAndLog(response);
        }

        /// <summary>
        ///     
        /// </summary>
        private void OnResultGridViewCellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            var dr = (_resultsGridView.Rows[e.RowIndex]
                .DataBoundItem as DataRowView).Row;
            var key = dr["key"].ToString();
            var response = _logResponses[key];

            string content = response.Content;

            try
            {
                var doc = new XmlDocument();

                doc.LoadXml(content);
                using (var form = new XmlForm(response))
                {
                    form.ShowDialog(this);
                }
            }
            catch
            {
                using (var form = new TextForm(content))
                {
                    form.ShowDialog(this);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnLogGridViewCellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            var dr = (_logGridView.Rows[e.RowIndex]
                .DataBoundItem as DataRowView).Row;
            var key = dr["key"].ToString();
            var request = _logRequests[key];
            using (var form = new TextForm(request))
            {
                form.ShowDialog(this);
            }
        }

        /// <summary>
        ///     
        /// </summary>
        /// <param name="sender" type="object">
        ///     
        /// </param>
        /// <param name="e" type="System.EventArgs">
        ///     
        /// </param>
        private void OnClearLogButtonClick(object sender, EventArgs e)
        {
            _logRequests.Clear();
            (_logGridView.DataSource as DataTable).Rows.Clear();
            _logResponses.Clear();
            (_resultsGridView.DataSource as DataTable).Rows.Clear();
        }

        /// <summary>
        ///     
        /// </summary>
        private void SanitizeApiInput()
        {
            _consumerKeyTextBox.Text = _consumerKeyTextBox.Text.Trim();
            _consumerSecretTextBox.Text = _consumerSecretTextBox.Text.Trim();
            _tokenTextBox.Text = _tokenTextBox.Text.Trim();
            _usernameTextBox.Text = _usernameTextBox.Text.Trim();
            _passwordTextBox.Text = _passwordTextBox.Text.Trim();
        }

        #region [ Public Methods ]

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnAddButtonClick(object sender, EventArgs e)
        {
            CallService(NumericOperator.Add);
        }

        /// <summary>
        /// 
        /// </summary>
        private void CallService(NumericOperator op)
        {
            try
            {
                var service = OAuthService.Create(
                    new EndPoint(_configs.OAuthRequestTokenUrl),
                    new Uri(_configs.OAuthAuthorizeUrl),
                    new EndPoint(_configs.OAuthAccessTokenUrl),
                    false,
                    string.Empty,
                    "HMAC-SHA1",
                    "1.0",
                    new OAuthConsumer(_consumerKeyTextBox.Text, _consumerSecretTextBox.Text)
                );

                // consume math service
                var request = OAuthRequest.Create(
                    new EndPoint(string.Format("{0}/{1}",
                        _configs.MathServiceUrl, op.ToString().ToLower()),
                        _getRadioButton.Checked ? "GET" : "POST"),
                    service);

                request.BeforeSendHttpWebRequest += OnBeforeSendHttpWebRequest;

                var parameters = new NameValueCollection()
                {
                    { "a", _aTextBox.Text },
                    { "b", _bTextBox.Text },
                };

                if (_xmlRadioButton.Checked)
                {
                    parameters.Add("format", "xml");
                }
                else if (_jsonRadioButton.Checked)
                {
                    parameters.Add("format", "json");
                }
                else if (_plainTextRadioButton.Checked)
                {
                    parameters.Add("format", "plain-text");
                }

                var reponse = request.GetPublicResource(parameters);

                request.BeforeSendHttpWebRequest -= OnBeforeSendHttpWebRequest;
                OnMathResponseReceived(reponse);
                
            }
            catch (Exception ex)
            {
                KryptonMessageBox.Show(ex.Message);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="response"></param>
        private void OnMathResponseReceived(OAuthResponse response)
        {
            var result = ParseResponseAndLog(response);

            if (string.Compare(response.ProtectedResource.ContentType, "text/html", false) == 0)
            {
                _resultTextBox.Text = result.Item1;
            }
            else if (string.Compare(response.ProtectedResource.ContentType, "text/xml", false) == 0)
            {
                //_resultTextBox.Text = result.Item1;
            }
            else if (string.Compare(response.ProtectedResource.ContentType, "application/json", false) == 0)
            {
                //_resultTextBox.Text = result.Item1;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="reponse"></param>
        private Tuple<string, NameValueCollection> ParseResponseAndLog(OAuthResponse reponse)
        {
            var dt = _resultsGridView.DataSource as DataTable;
            var dr = dt.NewRow();

            var key = Guid.NewGuid().ToString();
            var order = _resultsGridView.Rows.Count + 1;
            dr["order"] = order;
            dr["key"] = key;

            var headers = new NameValueCollection();
            var content = string.Empty;
            if (reponse.HasProtectedResource)
            {
                var resource = reponse.ProtectedResource;
                var ms = resource.GetResponseStream();
                using (var sr = new StreamReader(ms))
                {
                    content = sr.ReadToEnd();
                }

                Array.ForEach(resource.Headers.AllKeys, headerKey =>
                {
                    headers.Add(headerKey, resource.Headers[headerKey]);
                });
            }

            dr["content"] = content;
            dt.Rows.InsertAt(dr, 0);

            _logResponses.Add(key, new LogViewItem
            {
                Key = key,
                Order = order,
                Headers = headers,
                Content = content,
            });

            return new Tuple<string, NameValueCollection>(content, headers);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnBeforeSendHttpWebRequest(object sender, PreSendHttpWebRequestEventArgs e)
        {
            var dt = _logGridView.DataSource as DataTable;
            var dr = (_logGridView.DataSource as DataTable).NewRow();

            var order = _logGridView.Rows.Count + 1;
            var key = Guid.NewGuid().ToString();
            var url = string.Format("{0} {1}", e.Request.Method, e.Request.RequestUri.AbsoluteUri);
            dr["key"] = key;
            dr["order"] = order;
            dr["content"] = url;
            dt.Rows.InsertAt(dr, 0);

            var headers = new NameValueCollection();
            Array.ForEach(e.Request.Headers.AllKeys, headerKey =>
            {
                headers.Add(headerKey, e.Request.Headers[headerKey]);
            });

            _logRequests.Add(key, new LogViewItem
            {
                Key = key,
                Order = order,
                Headers = headers,
                Content = url,
                PostData = e.BodyContent,
            });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnSubtractButtonClick(object sender, EventArgs e)
        {
            CallService(NumericOperator.Subtract);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnMultiplyButtonClick(object sender, EventArgs e)
        {
            CallService(NumericOperator.Multiply);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnDivideButtonClick(object sender, EventArgs e)
        {
            CallService(NumericOperator.Divide);
        }
        #endregion
    }
}
